package cloudeval.spec

import cn.edu.neu.vmmigeval.cloud.entity.DataCenter
import cn.edu.neu.vmmigeval.cloud.entity.DataCenterBroker
import cn.edu.neu.vmmigeval.cloud.policy.WorkloadModel

/**
 * The base script definition for experiment.
 *
 * @author yfwz100
 */
abstract class ExperimentScript extends Script {

    private DataCenterSpec dataCenterSpec = new DataCenterSpec()
    private VMScheduleStrategySpec vmScheduleStrategySpec = new VMScheduleStrategySpec()
    private EvaluationSpec evaluationSpec = new EvaluationSpec()

    /**
     * Configure the data center: workload, hosts and VMs.
     *
     * @param closure the closure to create the data center specification.
     * @return the data center specification.
     */
    DataCenterSpec DataCenter(@DelegatesTo(DataCenterSpec) closure) {
        def code = closure.rehydrate(dataCenterSpec, this, this)
        code.resolveStrategy = Closure.DELEGATE_ONLY
        code()
        return dataCenterSpec
    }

    /**
     * Configure the schedule strategy to test.
     *
     * @param closure the closure to create the strategies specification.
     * @return strategies specification.
     */
    VMScheduleStrategySpec ScheduleStrategies(@DelegatesTo(VMScheduleStrategySpec) Closure closure) {
        def code = closure.rehydrate(vmScheduleStrategySpec, this, this)
        code.resolveStrategy = Closure.DELEGATE_ONLY
        code()
        return vmScheduleStrategySpec
    }

    /**
     * Configure the evaluation measures and metrics.
     *
     * @param closure the closure to create the evaluation specification.
     * @return the evaluation specification.
     */
    EvaluationSpec Evaluation(@DelegatesTo(EvaluationSpec) closure) {
        def code = closure.rehydrate(evaluationSpec, this, this)
        code.resolveStrategy = Closure.DELEGATE_ONLY
        code()
        return evaluationSpec
    }

    /**
     * Initialization of the configuration and run the experiments.
     */
    protected abstract void initConfiguration()

    // define the running cycle.
    int clockCycles = 1000

    @Override
    def run() {
        // initialize the configuration from DSL.
        initConfiguration()

        // loop of the experiment conditions.
        Map<String, Map<String, List<Double>>> table = new HashMap<>()

        for (hostCluster in dataCenterSpec.hostClusterSpecMap) {
            def hostClusterName = hostCluster.key
            def hostClusterSpec = hostCluster.value

            for (vmMap in dataCenterSpec.VMSpecMap) {
                def vmSpecName = vmMap.key
                def vmSpec = vmMap.value

                for (wl in dataCenterSpec.workloadSpecMap) {
                    def workloadName = wl.key
                    def workloadSpec = wl.value

                    for (strategy in vmScheduleStrategySpec.strategies) {
                        def strategyName = strategy.key
                        def vmAllocation = strategy.value

                        def name = [hostClusterName, vmSpecName, workloadName, strategyName].join('.')
                        def tab = table[name] = new HashMap<>()
                        evaluationSpec.measures.each { n, b ->
                            tab[n] = []
                        }
                        evaluationSpec.metrics.each { n, b ->
                            tab[n] = []
                        }

                        DataCenter dataCenter = hostClusterSpec.createDataCenter()
                        dataCenter.vmAllocation = vmAllocation

                        DataCenterBroker broker = vmSpec.createDataCenterBroker(dataCenter)

                        WorkloadModel workload = workloadSpec.createWorkloadModel()

                        for (int clock = 0; clock < clockCycles; clock ++) {
                            println("执行第 " + clock + " 个周期")

                            // update the data center and broker
                            dataCenter.updateAllMigration()
                            broker.updateVmsProcessing(clock)
                            dataCenter.updateHostsProcessing(clock)

                            vmAllocation.optimizeAllocation(dataCenter, clock)

                            // collect data.
                            evaluationSpec.measures.values()*.process(clock, broker, dataCenter)

                            // update workload.
                            workload.addCloudlets(clock, broker, dataCenter)

                            evaluationSpec.measures.each { n, b ->
                                tab[n].add(b.value)
                            }
                            evaluationSpec.metrics.each { n, b ->
                                tab[n].add(b.value)
                            }
                        }

                        // end of a case.
                    }
                }
            }
        }
        for (c in table) {
            PrintWriter writer = new PrintWriter(new FileWriter("${c.key}.csv"))

            def row = new ArrayList<String>(c.value.keySet())
            writer.println(row.join(','))
            for (int i=0; i<clockCycles; i++) {
                def b = []
                for (k in row) {
                    b.add(c.value[k][i])
                }
                writer.println(b.join(','))
            }
            writer.close()
        }
    }
}
